home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Glypha 3v2 / GlyphaIII Code ƒ / Sound.c < prev    next >
Text File  |  1995-06-30  |  14KB  |  378 lines

  1.  
  2. //============================================================================
  3. //----------------------------------------------------------------------------
  4. //                                    Sound.c
  5. //----------------------------------------------------------------------------
  6. //============================================================================
  7.  
  8. // This file handles all sound routines.  It handles 2 concurrent sound…
  9. // channels allowing 2 sounds to be played simultaneously.  It also handles…
  10. // a system of priorites whereby you can ensure that "important" sounds don't…
  11. // get cut off by "lesser" sounds.  In that there are 2 channels however,…
  12. // "lesser" sounds are not discounted outright - both channels are considered…
  13. // to determine if one of the channels is not playing at all (priority = 0) or…
  14. // playing a sound of an even lesser priority.  Make sense?
  15.  
  16. #include <Sound.h>
  17. #include "Externs.h"
  18.  
  19.  
  20. #define kMaxSounds                17            // Number of sounds to load.
  21. #define    kBaseBufferSoundID        1000        // ID of first sound (assumed sequential).
  22. #define kSoundDone                913            // Just a number I chose.
  23. #define kSoundDone2                749            // Just a number I chose.
  24.  
  25.  
  26. void PlaySound1 (short, short);
  27. void PlaySound2 (short, short);
  28. pascal void ExternalCallBack (SndChannelPtr, SndCommand);
  29. pascal void ExternalCallBack2 (SndChannelPtr, SndCommand);
  30. void LoadAllSounds (void);
  31. OSErr LoadBufferSounds (void);
  32. OSErr DumpBufferSounds (void);
  33. OSErr OpenSoundChannel (void);
  34. OSErr CloseSoundChannel (void);
  35.  
  36.  
  37. SndCallBackUPP        externalCallBackUPP, externalCallBackUPP2;
  38. SndChannelPtr        externalChannel, externalChannel2;
  39. Ptr                    theSoundData[kMaxSounds];
  40. short                externalPriority, externalPriority2;
  41. Boolean                channelOpen, soundOn;
  42.  
  43.  
  44. //==============================================================  Functions
  45. //--------------------------------------------------------------  PlaySound1
  46.  
  47. // This function takes a sound ID and a priority, and forces that sound to 
  48. // play through channel 1 - and saves the priority globally.  As well, a 
  49. // callback command is queues up in channel 1.
  50.  
  51. void PlaySound1 (short soundID, short priority)
  52. {
  53.     SndCommand    theCommand;
  54.     OSErr        theErr;
  55.     
  56.     theCommand.cmd = flushCmd;            // Send 1st a flushCmd to clear the sound queue.
  57.     theCommand.param1 = 0;
  58.     theCommand.param2 = 0L;
  59.     theErr = SndDoImmediate(externalChannel, &theCommand);
  60.     
  61.     theCommand.cmd = quietCmd;            // Send quietCmd to stop any current sound.
  62.     theCommand.param1 = 0;
  63.     theCommand.param2 = 0L;
  64.     theErr = SndDoImmediate(externalChannel, &theCommand);
  65.     
  66.     externalPriority = priority;        // Copy priority to global variable.
  67.     
  68.     theCommand.cmd = bufferCmd;            // Then, send a bufferCmd to channel 1.
  69.     theCommand.param1 = 0;                // The sound played will be soundID.
  70.     theCommand.param2 = (long)(theSoundData[soundID]);
  71.     theErr = SndDoImmediate(externalChannel, &theCommand);
  72.     
  73.     theCommand.cmd = callBackCmd;        // Lastly, queue up a callBackCmd to notify us…
  74.     theCommand.param1 = kSoundDone;        // when the sound has finished playing.
  75.     theCommand.param2 = SetCurrentA5();
  76.     theErr = SndDoCommand(externalChannel, &theCommand, TRUE);
  77. }
  78.  
  79. //--------------------------------------------------------------  PlaySound2
  80.  
  81. // This function is identical to the above function except that it handles…
  82. // playing sounds through channel 2.
  83.  
  84. void PlaySound2 (short soundID, short priority)
  85. {
  86.     SndCommand    theCommand;
  87.     OSErr        theErr;
  88.     
  89.     theCommand.cmd = flushCmd;            // Send 1st a flushCmd to clear the sound queue.
  90.     theCommand.param1 = 0;
  91.     theCommand.param2 = 0L;
  92.     theErr = SndDoImmediate(externalChannel2, &theCommand);
  93.     
  94.     theCommand.cmd = quietCmd;            // Send quietCmd to stop any current sound.
  95.     theCommand.param1 = 0;
  96.     theCommand.param2 = 0L;
  97.     theErr = SndDoImmediate(externalChannel2, &theCommand);
  98.     
  99.     externalPriority2 = priority;        // Copy priority to global variable.
  100.     
  101.     theCommand.cmd = bufferCmd;            // Then, send a bufferCmd to channel 1.
  102.     theCommand.param1 = 0;                // The sound played will be soundID.
  103.     theCommand.param2 = (long)(theSoundData[soundID]);
  104.     theErr = SndDoImmediate(externalChannel2, &theCommand);
  105.     
  106.     theCommand.cmd = callBackCmd;        // Lastly, queue up a callBackCmd to notify us…
  107.     theCommand.param1 = kSoundDone2;    // when the sound has finished playing.
  108.     theCommand.param2 = SetCurrentA5();
  109.     theErr = SndDoCommand(externalChannel2, &theCommand, TRUE);
  110. }
  111.  
  112. //--------------------------------------------------------  PlayExternalSound
  113.  
  114. // This function is probably poorly named for this application.  I lifted this…
  115. // whole library from one of my games and chopped it down for purposes of Glypha.
  116. // The original game treated "external" and "cockpit" sounds as seperate channels…
  117. // (such that cockpit sounds could only "override" other cockpit sounds and…
  118. // external sounds could only override other external sounds.
  119. // In any event, this is the primary function called from throughout Glypha.
  120. // This function is called with a sound ID and a priority (just some number) and…
  121. // the function then determines if one of the two sound channels is free to play…
  122. // the sound.  It determines this by way of priorities.  If a sound channel is…
  123. // idle and playing no sound, its channel priority is 0.  Since the priority of…
  124. // the sound you want to play is assumed to be greater than 0, it will, without…
  125. // a doubt, be allowed to play on an idle channel.  If however there is already…
  126. // a sound playing (the channel's priority is not equal to 0), the sound with the…
  127. // largest priority wins.  Mind you though that there are two channels to choose…
  128. // between.  Therefore, the function compares the priority passed in with the…
  129. // sound channel with the lowest priority.
  130.  
  131. void PlayExternalSound (short soundID, short priority)
  132. {                            // A little error-checking.
  133.     if ((soundID >= 0) && (soundID < kMaxSounds))
  134.     {
  135.         if (soundOn)        // More error-checking.
  136.         {                    // Find channel with lowest priority.
  137.             if (externalPriority < externalPriority2)
  138.             {                // Compare priority with that of channel 1.
  139.                 if (priority >= externalPriority)
  140.                     PlaySound1(soundID, priority);
  141.             }
  142.             else
  143.             {                // Compare priority with that of channel 2.
  144.                 if (priority >= externalPriority2)
  145.                     PlaySound2(soundID, priority);
  146.             }
  147.         }
  148.     }
  149. }
  150.  
  151. //--------------------------------------------------------  ExternalCallBack
  152.  
  153. // Callback routine.  If this looks ugly, blame Apple's Universal Headers.
  154. // The callback routine is called after a sound finishes playing.  The…
  155. // callback routine is extremely useful in that it enables us to know when…
  156. // to set the sound channels priority back to 0 (meaning no sound playing).
  157. // Keep in mind (by the way) that this funciton is called at interrupt time…
  158. // and thus may not cause memory to be moved.  Also, note that also because…
  159. // of the interupt situation, we need to handle setting A5 to point to our…
  160. // app's A5 and then set it back again.
  161.  
  162. RoutineDescriptor ExternalCallBackRD = 
  163.         BUILD_ROUTINE_DESCRIPTOR(uppFilePlayCompletionProcInfo, ExternalCallBack);
  164.  
  165. pascal void ExternalCallBack (SndChannelPtr theChannel, SndCommand theCommand)
  166. {
  167.     long        thisA5, gameA5;
  168.     
  169.     if (theCommand.param1 == kSoundDone)    // See if it's OUR callback.
  170.     {
  171.         gameA5 = theCommand.param2;            // Extract our A5 from sound command.
  172.         thisA5 = SetA5(gameA5);                // Point A5 to our app (save off current A5).
  173.         
  174.         externalPriority = 0;                // Set global to reflect no sound playing.
  175.         
  176.         thisA5 = SetA5(thisA5);                // Restire A5.
  177.     }
  178. }
  179.  
  180. //--------------------------------------------------------  ExternalCallBack2
  181.  
  182. // This function is identical to the above function but handles sound channel 2.
  183.  
  184. RoutineDescriptor ExternalCallBackRD2 = 
  185.         BUILD_ROUTINE_DESCRIPTOR(uppFilePlayCompletionProcInfo, ExternalCallBack2);
  186.  
  187. pascal void ExternalCallBack2 (SndChannelPtr theChannel, SndCommand theCommand)
  188. {
  189.     long        thisA5, gameA5;
  190.     
  191.     if (theCommand.param1 == kSoundDone2)    // See if it's OUR callback.
  192.     {
  193.         gameA5 = theCommand.param2;            // Extract our A5 from sound command.
  194.         thisA5 = SetA5(gameA5);                // Point A5 to our app (save off current A5).
  195.         
  196.         externalPriority2 = 0;                // Set global to reflect no sound playing.
  197.         
  198.         thisA5 = SetA5(thisA5);                // Restire A5.
  199.     }
  200. }
  201.  
  202. //--------------------------------------------------------  LoadBufferSounds
  203.  
  204. // This function loads up all the sounds we'll need in the game and then…
  205. // strips off their header so that we can pass them as buffer commands.
  206. // Sounds are stored in our resource fork as 'snd ' resources.  There is a…
  207. // 20 byte header that we need to remove in order to use bufferCmd's.
  208. // This function is called only once, when the game loads up.
  209.  
  210. OSErr LoadBufferSounds (void)
  211. {
  212.     Handle        theSound;
  213.     long        soundDataSize;
  214.     OSErr        theErr;
  215.     short        i;
  216.     
  217.     theErr = noErr;                        // Assume no errors.
  218.     
  219.     for (i = 0; i < kMaxSounds; i++)    // Walk through all sounds.
  220.     {                                    // Load 'snd ' from resource.
  221.         theSound = GetResource('snd ', i + kBaseBufferSoundID);
  222.         if (theSound == 0L)                // Make sure it loaded okay.
  223.             return (ResError());        // Return reason it failed (if it did).
  224.         
  225.         HLock(theSound);                // If we got this far, lock sound down.
  226.                                         // Calculate size of sound minus header.
  227.         soundDataSize = GetHandleSize(theSound) - 20L;
  228.         HUnlock(theSound);                // Okay, unlock.
  229.                                         // Create pointer the size calculated above.
  230.         theSoundData[i] = NewPtr(soundDataSize);
  231.         if (theSoundData[i] == 0L)        // See if we created it okay.
  232.             return (MemError());        // If failed, return the reason why.
  233.         HLock(theSound);                // Okay, lock the sound handle again.
  234.                                         // Copy sound data (minus header) to our pointer.
  235.         BlockMove((Ptr)(*theSound + 20L), theSoundData[i], soundDataSize);
  236.         HUnlock(theSound);                // Unlock sound handle again.
  237.         ReleaseResource(theSound);        // And toss it from memory.
  238.     }
  239.     
  240.     return (theErr);
  241. }
  242.  
  243. //--------------------------------------------------------  DumpBufferSounds
  244.  
  245. // This function is called when Glypha exits (quits).  All those nasty pointers…
  246. // we created in the above function are reclaimed.
  247.  
  248. OSErr DumpBufferSounds (void)
  249. {
  250.     OSErr        theErr;
  251.     short        i;
  252.     
  253.     theErr = noErr;
  254.     
  255.     for (i = 0; i < kMaxSounds; i++)        // Go through all sound pointers.
  256.     {
  257.         if (theSoundData[i] != 0L)            // Make sure it exists.
  258.             DisposPtr(theSoundData[i]);        // Dispose of it.
  259.         theSoundData[i] = 0L;                // Make sure it reflects its "nonexistence".
  260.     }
  261.     
  262.     return (theErr);
  263. }
  264.  
  265. //--------------------------------------------------------  OpenSoundChannel
  266.  
  267. // This should perhaps be called OpenSoundChannels() since it opens two.
  268. // It is called once (at initialization) to set up the two sound channels…
  269. // we will use throughout Glypha.  For purposes of speed, 8-bit sound channels…
  270. // with no interpolation and monophonic are opened.  They'll use the sampled…
  271. // synthesizer (digitized sound) and be assigned their respective callback…
  272. // routines.
  273.  
  274. OSErr OpenSoundChannel (void)
  275. {
  276.     OSErr        theErr;
  277.     
  278.     #if USESROUTINEDESCRIPTORS
  279.         externalCallBackUPP = &ExternalCallBackRD;    // Handle Universal Header ugliness.
  280.         externalCallBackUPP2 = &ExternalCallBackRD2;
  281.     #else
  282.         externalCallBackUPP = (SndCallBackUPP) &ExternalCallBack;
  283.         externalCallBackUPP2 = (SndCallBackUPP) &ExternalCallBack2;
  284.     #endif
  285.     
  286.     theErr = noErr;                                    // Assume no errors.
  287.     
  288.     if (channelOpen)                                // Error checking.
  289.         return (theErr);
  290.     
  291.     externalChannel = 0L;
  292.     theErr = SndNewChannel(&externalChannel,         // Open channel 1.
  293.             sampledSynth, initNoInterp + initMono, 
  294.             (SndCallBackUPP)externalCallBackUPP);
  295.     if (theErr == noErr)                            // See if it worked.
  296.         channelOpen = TRUE;
  297.     
  298.     externalChannel2 = 0L;
  299.     theErr = SndNewChannel(&externalChannel2,         // Open channel 2.
  300.             sampledSynth, initNoInterp + initMono, 
  301.             (SndCallBackUPP)externalCallBackUPP2);
  302.     if (theErr == noErr)                            // See if it worked.
  303.         channelOpen = TRUE;
  304.     
  305.     return (theErr);
  306. }
  307.  
  308. //--------------------------------------------------------  CloseSoundChannel
  309.  
  310. // This function is called only upon quitting Glypha.  Both sound channels…
  311. // we created above are closed down.
  312.  
  313. OSErr CloseSoundChannel (void)
  314. {
  315.     OSErr        theErr;
  316.     
  317.     theErr = noErr;
  318.     
  319.     if (!channelOpen)            // Error checking.
  320.         return (theErr);
  321.     
  322.     if (externalChannel != 0L)    // Dispose of channel 1 (if open).
  323.         theErr = SndDisposeChannel(externalChannel, TRUE);
  324.     externalChannel = 0L;        // Flag it closed.
  325.     
  326.     if (externalChannel2 != 0L)    // Dispose of channel 2 (if open).
  327.         theErr = SndDisposeChannel(externalChannel2, TRUE);
  328.     externalChannel2 = 0L;        // Flag it closed.
  329.     
  330.     if (theErr == noErr)
  331.         channelOpen = FALSE;
  332.     
  333.     return (theErr);
  334. }
  335.  
  336. //--------------------------------------------------------  InitSound
  337.  
  338. // All the above initialization routines are handled by this one function.
  339. // This single function is the only one that needs to be called - it handles…
  340. // calling the functions that load the sounds and create the sound channels.
  341. // It is called from main() when Glypha is loading up and going through its…
  342. // initialization phase.
  343.  
  344. void InitSound (void)
  345. {
  346.     OSErr        theErr;
  347.     
  348.     soundOn = TRUE;            // Note that initialization of sounds has occurred…
  349.                             // (or rather is just about to this instant!).
  350.     externalChannel = 0L;    // Flag channels as nonexistant.
  351.     externalChannel2 = 0L;
  352.     externalPriority = 0;    // Set priorities to 0 (no sound playing).
  353.     externalPriority2 = 0;
  354.                             // Load up all sounds (see above function).
  355.     theErr = LoadBufferSounds();
  356.     if (theErr != noErr)    // If it fails, we'll quit Glypha.
  357.         RedAlert("\pFailed Loading Sounds");
  358.                             // Open up the two sound channels.
  359.     theErr = OpenSoundChannel();
  360.     if (theErr != noErr)    // If that fails we'll quit Glypha as well.
  361.         RedAlert("\pFailed To Open Sound Channels");
  362. }
  363.  
  364. //--------------------------------------------------------  KillSound
  365.  
  366. // Complementary to the above function, this one is called only when Glypha…
  367. // quits and it handles all the "shut-down" routines.  It also is called from…
  368. // main(), but it is called last - just as Glypha is quitting.
  369.  
  370. void KillSound (void)
  371. {
  372.     OSErr        theErr;
  373.     
  374.     theErr = DumpBufferSounds();    // Kill all sound pointers.
  375.     theErr = CloseSoundChannel();    // Close down the sound channels.
  376. }
  377.  
  378.